/*
    环境搭建与开发环境配置
    https://blog.csdn.net/howard2005/article/details/121945757

    https://dart.dev/get-dart
    https://gekorm.com/dart-windows/

    https://dart.cn/get-dart/archive
    https://dart.cn/guides/environment-declarations

    https://dart.cn/samples
 */

/*
  变量
  虽然 Dart 是代码类型安全的语言，但是由于其支持类型推断，因此大多数变量不需要显式地指定类型：
 */
import 'PilotedCraft.dart';
import 'Spacecraft.dart';

var name = 'Voyager I';
var nameArray = ['demo1', "demo2"];
var antennaDiameter = 3.7;
var user = {'name': 'test01', 'id': 001};
var year = 1977;
var flybyObjects = ['Jupiter', 'Saturn', 'Uranus', 'Neptune'];

var image = {
  'tag': ['saturn'],
  'url': '//path/to/saturn.jpg'
};

void main() {
  // print("dart -> hello word");
  // print(user.length);

  print('-------------------  流程控制语句  -----------------------');

  if (year >= 2001) {
    print('21 century');
  } else if (year >= 1901) {
    print('20 century');
  }

  print('-------------------------------------------------------------------');

  for (final obj in flybyObjects) {
    print(obj);
  }

  print('-------------------------------------------------------------------');

  for (int month = 1; month <= 12; month++) {
    print(month);
  }

  print('-------------------------------------------------------------------');

  while (year < 2016) {
    year += 1;
  }

  print(year);

  print('-------------------------------------------------------------------');

  var resultfibonacci = fibonacci(20);
  print('计算结果：' + resultfibonacci.toString());
  //计算结果：6765
  print('-------------------------------------------------------------------');

  //=> (胖箭头) 简写语法用于仅包含一条语句的函数。该语法在将匿名函数作为参数传递时非常有用：
  flybyObjects.where((name) => name.contains('turn')).forEach(print);
  //Saturn

  print('-------------------------------------------------------------------');

  var voyager = Spacecraft('Voyager I', DateTime(1977, 9, 5));
  voyager.describe();
  print('-------------------------------------------------------------------');

  var voyager3 = Spacecraft.unlaunched('Voyager III');

  voyager3.describe();

  print('-------------------------------------------------------------------');

  //Mixins
  var pilotedCraft = PilotedCraft('pilotedCraft I', DateTime(2000, 4, 23));
  pilotedCraft.describe();
  pilotedCraft.describeCrew();

  print('-------------------------------------------------------------------');

  printWithDelay("演示 异步");

  /*

    Dart 开发语言概览

    https://dart.cn/guides/language/language-tour
   */

  int aNumber = 2;
  var number3 = 3;
  print('aNumber = $aNumber');
  //print()
  // 一种便利的将信息输出显示的方式。
  //
  // '...' (或 "...")
  // 表示字符串字面量。
  //
  // $variableName (或 ${expression})
  // 表示字符串插值：字符串字面量中包含的变量或表达式。查阅字符串获取更多相关信息。


  /*
      重要概念

       所有变量引用的都是 对象，每个对象都是一个 类 的实例。数字、函数以及 null 都是对象。除去 null 以外（如果你开启了 空安全）, 所有的类都继承于 Object 类。

      1、尽管 Dart 是强类型语言，但是在声明变量时指定类型是可选的，因为 Dart 可以进行类型推断。在上述代码中，变量 number3 的类型被推断为 int 类型。

      2、如果你开启了 空安全，变量在未声明为可空类型时不能为 null。你可以通过在类型后加上问号 (?) 将类型声明为可空。例如，int? 类型的变量可以是整型数字或 null。

      如果你 明确知道 一个表达式不会为空，但 Dart 不这么认为时，你可以在表达式后添加 ! 来断言表达式不为空（为空时将抛出异常）。例如：int x = nullableButNotNullInt!

     3、如果你想要显式地声明允许任意类型，使用 Object?（如果你 开启了空安全）、 Object 或者 特殊类型 dynamic 将检查延迟到运行时进行。

     4、Dart 支持泛型，比如 List<int>（表示一组由 int 对象组成的列表）或 List<Object>（表示一组由任何类型对象组成的列表）。

     5、Dart 支持顶级函数（例如 main 方法），同时还支持定义属于类或对象的函数（即 静态 和 实例方法）。你还可以在函数中定义函数（嵌套 或 局部函数）

     6、Dart 支持顶级 变量，以及定义属于类或对象的变量（静态和实例变量）。实例变量有时称之为域或属性。

     7、Dart 没有类似于 Java 那样的 public、protected 和 private 成员访问限定符。如果一个标识符以下划线 (_) 开头则表示该标识符在库内是私有的。可以查阅 库和可见性 获取更多相关信息。

     8、标识符 可以以字母或者下划线 (_) 开头，其后可跟字符和数字的组合。

     9、Dart 中 表达式 和 语句 是有区别的，表达式有值而语句没有。比如条件表达式 expression condition ? expr1 : expr2 中含有值 expr1 或 expr2。与 if-else 分支语句相比，if-else 分支语句则没有值。
     一个语句通常包含一个或多个表达式，但是一个表达式不能只包含一个语句。

     10、Dart 工具可以显示 警告 和 错误 两种类型的问题。警告表明代码可能有问题但不会阻止其运行。错误分为编译时错误和运行时错误；编译时错误代码无法运行；运行时错误会在代码运行时导致 异常。



   */




  //默认值
  // 在 Dart 中，未初始化以及可空类型的变量拥有一个默认的初始值 null。（如果你未迁移至 空安全，所有变量都为可空类型。）即便数字也是如此，因为在 Dart 中一切皆为对象，数字也不例外。
  int? lineCount;
  // 备注:
  // assert() 的调用将会在生产环境的代码中被忽略掉。在开发过程中，assert(condition) 将会在 条件判断 为 false 时抛出一个异常。详情请查阅 Assert。
  assert(lineCount == null);


  //若你启用了空安全，你必须在使用变量前初始化它的值。
  lineCount = 1;

  //并不需要在声明变量时初始化，只需在第一次用到这个变量前初始化即可。例如，下面的代码是正确的，因为 Dart 可以在 lineCount 被传递到 print() 时检测它是否为空:
  int lineCount2;
  bool weLikeToCount = false;

  if(weLikeToCount) {
    lineCount = countLines();
  }else{
    lineCount2 = 0;
  }



  /*
      延迟初始化变量
      Dart 2.12 新增了 late 修饰符，这个修饰符可以在以下情况中使用：

      声明一个非空变量，但不在声明时初始化。

      延迟初始化一个变量。

      通常 Dart 的语义分析会在一个已声明为非空的变量被使用前检查它是否已经被赋值，但有时这个分析会失败。例如：在检查顶级变量和实例变量时，分析通常无法判断它们是否已经被初始化，因此不会进行分析。

      如果你确定这个变量在使用前就已经被声明，但 Dart 判断失误的话，你可以在声明变量的时候使用 late 修饰来解决这个问题。



      如果一个 late 修饰的变量在声明时就指定了初始化方法，那么它实际的初始化过程会发生在第一次被使用的时候。这样的延迟初始化在以下场景中会带来便利：

      Dart 认为这个变量可能在后文中没被使用，而且初始化时将产生较大的代价。

      你正在初始化一个实例变量，它的初始化方法需要调用 this。

      在下面这个例子中，如果 temperature 变量从未被使用的话，那么 readThermometer() 将永远不会被调用：

      // This is the program's only call to readThermometer().
      late String temperature = readThermometer(); // Lazily initialized.

   */

  late String description;
  description = "Feijoada";
  print('description 的 文案：$description');



  /*
      Final 和 Const
      如果你不想更改一个变量，可以使用关键字 final 或者 const 修饰变量，这两个关键字可以替代 var 关键字或者加在一个具体的类型前。
      一个 final 变量只可以被赋值一次；一个 const 变量是一个编译时常量 (const 变量同时也是 final 的)。

       备注:

        实例变量 可以是 final 的但不可以是 const，

        初始化实例变量的方式
        https://www.jianshu.com/p/7f61960965c3
   */

 // const var tempNumber = 1; // 这里会报错，因为是 var(实例变量) 前面不能用 const


  final name1 = 'Bob';
  final String nickname = 'Bobby';

  /*
      使用关键字 const 修饰变量表示该变量为 编译时常量。如果使用 const 修饰类中的变量，则必须加上 static 关键字，即 static const（译者注：顺序不能颠倒）。
      在声明 const 变量时可以直接为其赋值，也可以使用其它的 const 变量为其赋值：
   */

  const bar = 1000000;
  const double  atm = 1.01325 * bar;

  /*
      const 关键字不仅仅可以用来定义常量，还可以用来创建 常量值，该常量值可以赋予给任何变量。你也可以将构造函数声明为 const 的，这种类型的构造函数创建的对象是不可改变的。
   */

  var foo = const[];
  final bar2 = const[];
  const baz = [];

  /*
      如果使用初始化表达式为常量赋值可以省略掉关键字 const，比如上面的常量 baz 的赋值就省略掉了 const。详情请查阅 不要冗余地使用 const。

      没有使用 final 或 const 修饰的变量的值是可以被更改的，即使这些变量之前引用过 const 的值。

   */

  foo = [1,2,3];// Was const []

  //常量的值不可以被修改
 //  baz = [42];



}

int countLines(){
  return 0;
}

/*
    函数
    建议 为每个函数的参数以及返回值都指定类型：



    fibonacci : 斐波纳契（一种整数数列）

    斐波那契数列: https://baike.baidu.com/item/%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%95%B0%E5%88%97/99145?fr=aladdin
    数学术语
    斐波那契数列（Fibonacci sequence），又称黄金分割数列，因数学家莱昂纳多·斐波那契（Leonardo Fibonacci）以兔子繁殖为例子而引入，故又称为“兔子数列”，
    指的是这样一个数列：1、1、2、3、5、8、13、21、34、……在数学上，
    斐波那契数列以如下被以递推的方法定义：F(0)=1，F(1)=1, F(n)=F(n - 1)+F(n - 2)（n ≥ 2，n ∈ N*）在现代物理、准晶体结构、化学等领域，
    斐波那契数列都有直接的应用，为此，美国数学会从 1963 年起出版了以《斐波那契数列季刊》为名的一份数学杂志，用于专门刊载这方面的研究成果
   */
int fibonacci(int n) {
  if (n == 0 || n == 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
}

/*
    注释
    Dart 通常使用双斜杠 // 作为注释的开始。


    /// 这是一个文档注释。
    /// 文档注释用于为库、类以及类的成员添加注释。
    /// 像 IDE 和 dartdoc 这样的工具可以专门处理文档注释。

    /* 也可以像这样使用单斜杠和星号的注释方式 */
 */

/*
  异步
 使用 async 和 await 关键字可以让你避免回调地狱 (Callback Hell) 并使你的代码更具可读性。

 */

const oneSecond = Duration(seconds: 1);

Future<void> printWithDelay(String message) async {
  await Future.delayed(oneSecond);
  print(message);
}

//上面的方法相当于：
Future<void> printWithDelay2(String message) {
  // return Future.delayed(oneSecond).then((_) => print(message));
  return Future.delayed(oneSecond).then((_) {
    print(message);
  });
}

//如下一个示例所示，async 和 await 关键字有助于使异步代码变得易于阅读。
// Future<void> createDescriptions(Iterable<String> objects) async {
//   for (final object in objects) {
//     try {
//       var file = File('$object.txt');
//       if (await file.exists()) {
//         var modified = await file.lastModified();
//         print(
//             'File for $object already exists. It was modified on $modified.');
//         continue;
//       }
//       await file.create();
//       await file.writeAsString('Start describing $object in this file.');
//     } on IOException catch (e) {
//       print('Cannot create description for $object: $e');
//     }
//   }
// }

/*
  异常
  使用 throw 关键字抛出一个异常：

    if (astronauts == 0) {
      throw StateError('No astronauts.');
    }

  使用 try 语句配合 on 或 catch（两者也可同时使用）关键字来捕获一个异常:
 */